﻿using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace XChipTool
{

    public enum BaseRegisterMap : uint
    {
        IR_BASE_ADDR = 0xC2000400,
        SPI_BASE_ADDR = 0x82000000,
        SDRAM_BASE_ADDR = 0xC4000000,
    }
    public enum SPIRegisterMap : uint
    {
        SPI_FUNCTION_SELECT = 0x00000000,
        SPI_CONTROL = 0x00000004,
        SPI_RESERVED = 0x00000008,
        SPI_SHIFT_CONTROL = 0x0000000c,
        SPI_SHIFT_TXD0 = 0x00000010,
        SPI_SHIFT_TXD1 = 0x00000014,
        SPI_SHIFT_TXD2 = 0x00000018,
        SPI_SHIFT_TXD3 = 0x0000001C,
        SPI_SHIFT_RXD0 = 0x00000020,
        SPI_SHIFT_RXD1 = 0x00000024,
        SPI_SHIFT_RXD2 = 0x00000028,
        SPI_SHIFT_RXD3 = 0x0000002C,
        SPI_INT_STATUS = 0x00000030,
        SPI_INT_MASK = 0x00000034,
        SPI_BUSY = 0x00000038,
        SPI_BUSY_FM = 0x00000040
    }

    public class FlashBase
    {
        SerialPort comm;
        public FlashBase(SerialPort _comm)
        {
            comm = _comm;
        }

        #region 全局变量
        uint FUNCT_SEL_ADDR = (uint)((long)BaseRegisterMap.SPI_BASE_ADDR + (uint)SPIRegisterMap.SPI_FUNCTION_SELECT);
        //uint CONTROL_ADDR = (uint)((long)BaseRegisterMap.SPI_BASE_ADDR + (uint)SPIRegisterMap.SPI_CONTROL);
        uint SHIFT_ADDR = (uint)((long)BaseRegisterMap.SPI_BASE_ADDR + (uint)SPIRegisterMap.SPI_SHIFT_CONTROL);
        uint TXD0_ADDR = (uint)((long)BaseRegisterMap.SPI_BASE_ADDR + (uint)SPIRegisterMap.SPI_SHIFT_TXD0);
        //uint TXD1_ADDR = (uint)((long)BaseRegisterMap.SPI_BASE_ADDR + (uint)SPIRegisterMap.SPI_SHIFT_TXD1);
        uint RXD0_ADDR = (uint)((long)BaseRegisterMap.SPI_BASE_ADDR + (uint)SPIRegisterMap.SPI_SHIFT_RXD0);
        //uint RXD1_ADDR = (uint)((long)BaseRegisterMap.SPI_BASE_ADDR + (uint)SPIRegisterMap.SPI_SHIFT_RXD1);
        #endregion

        public delegate void SendEventHandler(string msg);

        public event SendEventHandler SendEvent;



        public byte SendRegData(byte a, uint ADDR, uint DATA, SerialPort comm)
        {

            byte[] sendbuf = new byte[9];
            byte[] buf = new byte[9]; //声明一个临时数组存储当前来的串口数据     
            byte value = 0x00;
            int i;
          
            comm.DiscardInBuffer();           

            //TXD0 ADDRESS
            sendbuf[0] = a;//com choose write or byte write
                           //TXD0 ADDRESS
            sendbuf[1] = (byte)((ADDR >> 0) & 0xff);
            sendbuf[2] = (byte)((ADDR >> 8) & 0xff);
            sendbuf[3] = (byte)((ADDR >> 16) & 0xff);
            sendbuf[4] = (byte)((ADDR >> 24) & 0xff);
            //大端模式
            sendbuf[5] = (byte)((DATA >> 24) & 0xff);
            sendbuf[6] = (byte)((DATA >> 16) & 0xff);
            sendbuf[7] = (byte)((DATA >> 8) & 0xff);
            sendbuf[8] = (byte)((DATA >> 0) & 0xff);
            //TXD0 REG DATA
            //sendbuf[5] = (byte)((DATA >> 0) & 0xff);
            //sendbuf[6] = (byte)((DATA >> 8) & 0xff);
            //sendbuf[7] = (byte)((DATA >> 16) & 0xff);
            //sendbuf[8] = (byte)((DATA >> 24) & 0xff);
            if (comm.IsOpen == true)
            {
                CommRecvBufClear(comm);
                //串口写
                comm.Write(sendbuf, 0, 9);
                // this.textBox_disp.AppendText("Write Data: ");
                //data_disp(sendbuf, 9);

                for (i = 0; i < 100; i++)
                {
                    if (comm.BytesToRead > 0)
                    {
                        int n = comm.BytesToRead;//先记录下来，避免某种原因，人为的原因，操作几次之间时间长，缓存不一致 
                        if (n >= 8)
                            n = 8;
                        var num = comm.Read(buf, 0, n);//读取缓冲数据
                                                       // this.textBox_disp.AppendText("Recv Data: ");
                                                       //data_disp(buf, n);
                        value = buf[0];

                        break;
                    }
                    else
                        Thread.Sleep(2);
                }
            }
            else
            {
                if (this.SendEvent != null)
                {
                    this.SendEvent("串口关闭状态，请检查！");
                }
            }
            if (value != a)
            {
                StringBuilder sb = new StringBuilder();
                sb.Append(string.Format("{0}{1}", "fail in send:", ADDR.ToString("X2")));
                if (string.IsNullOrEmpty(sb.ToString()))
                {
                    if (this.SendEvent != null)
                    {
                        sb.Append("\r\n");
                        this.SendEvent(sb.ToString());
                    }
                }
            }
            return value;

        }

        public byte ReadRegData(byte a, uint ADDR, ref uint DATA, SerialPort comm)
        {
            byte[] readbuf = new byte[15];
            byte[] buf = new byte[10];
            byte value = 0x00;
            int recv_cnt = 0x00;
            uint byte_sel = ADDR & 0x3;
            //byte_sel = (3 - byte_sel) * 8;
            comm.DiscardInBuffer();

            //TXD0 ADDRESS
            readbuf[0] = a;
            //TXD0 ADDRESS
            readbuf[1] = (byte)((ADDR >> 0) & 0xff);
            readbuf[2] = (byte)((ADDR >> 8) & 0xff);
            readbuf[3] = (byte)((ADDR >> 16) & 0xff);
            readbuf[4] = (byte)((ADDR >> 24) & 0xff);
            if (comm.IsOpen == true)
            {
                CommRecvBufClear(comm);
                //串口写
                comm.Write(readbuf, 0, 5);
                //data_disp(readbuf, 5);
                DATA = 0;
                int i;
                for (i = 0; i < 100; i++)
                {
                    if (comm.BytesToRead > 0)
                    {
                        int n = comm.BytesToRead;//先记录下来，避免某种原因，人为的原因，操作几次之间时间长，缓存不一致     
                        //声明一个临时数组存储当前来的串口数据   

                        if (n >= 8) n = 8;
                        comm.Read(buf, 0, n);//读取缓冲数据

                        if ((recv_cnt + n) >= 12)
                        {
                            n = 12 - recv_cnt;
                        }

                        for (int j = 0; j < n; j++)
                        {
                            readbuf[recv_cnt + j] = buf[j];
                        }
                        recv_cnt += n;
                        if (recv_cnt >= 5)
                        {
                            //this.textBox_disp.AppendText("Recv Read Data: ");
                            //data_disp(readbuf, recv_cnt);
                            value = readbuf[0];

                            if (readbuf[0] == 0x11)
                                DATA = (uint)((readbuf[1] << 24) + (readbuf[2] << 16) + (readbuf[3] << 8) + (readbuf[4] << 0));
                            else
                            {
                                switch (byte_sel)
                                {
                                    case 0:
                                        DATA = readbuf[4];
                                        break;
                                    case 1:
                                        DATA = readbuf[3];
                                        break;
                                    case 2:
                                        DATA = readbuf[2];
                                        break;
                                    case 3:
                                        DATA = readbuf[1];
                                        break;
                                    default:
                                        DATA = 0x0;
                                        break;
                                }

                            }
                            break;
                        }

                    }
                    else
                        Thread.Sleep(2);

                }
            }
            else
            {
                if (this.SendEvent != null)
                {
                    this.SendEvent("串口关闭状态，请检查！");
                }
            }
            return value;
        }
        public  byte ReadRegDataARM(byte a, uint ADDR, ref uint DATA, SerialPort sPort)
        {
            byte[] readbuf = new byte[15];
            byte[] buf = new byte[10];
            byte value = 0x00;
            int recv_cnt = 0x00;
            uint byte_sel = ADDR & 0x3;
            //byte_sel = (3 - byte_sel) * 8;

            //TXD0 ADDRESS
            readbuf[0] = a;
            //TXD0 ADDRESS
            readbuf[1] = (byte)((ADDR >> 0) & 0xff);
            readbuf[2] = (byte)((ADDR >> 8) & 0xff);
            readbuf[3] = (byte)((ADDR >> 16) & 0xff);
            readbuf[4] = (byte)((ADDR >> 24) & 0xff);

            //DiscardInBuffer();
            sPort.Write(readbuf, 0, 5);
            //if (null != SendSerialSendClick && isDebug)
            //{
            //    SendSerialSendClick($" Send Read Cmd: {bufMsg(readbuf, 5)}");
            //}
            //SengMsg($" Send Read Cmd: {bufMsg(readbuf, 5)}");
            int i;
            for (i = 0; i < 100; i++)
            {
                if (sPort.BytesToRead > 0)
                {
                    int n = sPort.BytesToRead;//先记录下来，避免某种原因，人为的原因，操作几次之间时间长，缓存不一致     
                                              //声明一个临时数组存储当前来的串口数据   

                    if (n >= 8)
                        n = 8;
                    sPort.Read(buf, 0, n);//读取缓冲数据

                    if ((recv_cnt + n) >= 12)
                    {
                        n = 12 - recv_cnt;
                    }

                    for (int j = 0; j < n; j++)
                    {
                        readbuf[recv_cnt + j] = buf[j];
                    }
                    recv_cnt += n;
                    if (recv_cnt >= 5)
                    {
                        //if (null != SendSerialSendClick && isDebug)
                        //{
                        //    SendSerialSendClick($" Recv Read Data: {bufMsg(readbuf, recv_cnt)}");
                        //}
                        //SengMsg($" Recv Read Data: {bufMsg(readbuf, recv_cnt)}");
                        value = readbuf[0];

                        if (readbuf[0] == 0x11)
                            DATA = (uint)((readbuf[1] << 0) + (readbuf[2] << 8) + (readbuf[3] << 16) + (readbuf[4] << 24));
                        else
                        {
                            switch (byte_sel)
                            {
                                case 0:
                                    DATA = readbuf[1];
                                    break;
                                case 1:
                                    DATA = readbuf[2];
                                    break;

                                case 2:
                                    DATA = readbuf[3];
                                    break;
                                case 3:
                                    DATA = readbuf[4];
                                    break;
                                default:
                                    DATA = 0x0;
                                    break;
                            }

                        }
                        break;
                    }

                }
                else
                    Thread.Sleep(10);
            }


            if (value != a)
            {
                if (null != SendEvent )
                {
                    SendEvent($"fail in send: {ADDR.ToString("X2")}\r\n");
                }
                //SengMsg($"fail in send: {ADDR.ToString("X2")}\r\n");
            }
            else if (DATA.ToString("X2")=="FFFFFFFF")
            {

            }
            else
            {
                if (null != SendEvent  && a != 0x1)
                {
                    SendEvent($"R:0x{ADDR.ToString("X8")} 0x{DATA.ToString("X2")}");
                }
            }
            return value;
        } 
        public  byte SendRegDataARM(byte a, uint ADDR, uint DATA, SerialPort sPort)
        {

            byte[] sendbuf = new byte[9];
            byte[] buf = new byte[9]; //声明一个临时数组存储当前来的串口数据     
            byte value = 0x00;
            int i;
            //TXD0 ADDRESS
            sendbuf[0] = a;//com choose write or byte write
            //TXD0 ADDRESS
            sendbuf[1] = (byte)((ADDR >> 0) & 0xff);
            sendbuf[2] = (byte)((ADDR >> 8) & 0xff);
            sendbuf[3] = (byte)((ADDR >> 16) & 0xff);
            sendbuf[4] = (byte)((ADDR >> 24) & 0xff);
            //小端模式
            sendbuf[5] = (byte)((DATA >> 0) & 0xff);
            sendbuf[6] = (byte)((DATA >> 8) & 0xff);
            sendbuf[7] = (byte)((DATA >> 16) & 0xff);
            sendbuf[8] = (byte)((DATA >> 24) & 0xff);

            sPort.Write(sendbuf, 0, 9);
            //if (null != SendSerialSendClick && isDebug)
            //{
            //    SendSerialSendClick($" Send Read Cmd: {bufMsg(sendbuf, 9)}");
            //}
            //SengMsg($" Send Read Cmd: {bufMsg(sendbuf, 9)}");

            for (i = 0; i < 100; i++)
            {
                if (sPort.BytesToRead > 0)
                {
                    int n = sPort.BytesToRead;//先记录下来，避免某种原因，人为的原因，操作几次之间时间长，缓存不一致 
                    if (n >= 8)
                        n = 8;
                    sPort.Read(buf, 0, n);//读取缓冲数据
                    //if (null != SendSerialSendClick && isDebug)
                    //{
                    //    SendSerialSendClick($" Recv Data: {bufMsg(buf, n)}");
                    //}
                    //SengMsg($" Recv Data: {bufMsg(buf, n)}");
                    value = buf[0];

                    break;
                }
                else
                    Thread.Sleep(10);
            }

            if (value != a)
            {
                if (null != SendEvent)
                {
                    SendEvent($"fail in send: {ADDR.ToString("X2")}\r\n");
                }
                //SengMsg($"fail in send: {ADDR.ToString("X2")}\r\n");
            }
            else if (value==0xff)
            {

            }
            else
            {
                if (null != SendEvent  && a != 0x1)
                {
                    int byte_sel = (int)(ADDR) & 0x3;
                    byte_sel = byte_sel * 8;
                    var reg_data = DATA >> byte_sel;
                    reg_data = reg_data & 0xff;
                    SendEvent($"W:0x{ADDR.ToString("X8")} 0x{reg_data.ToString("X2")}");

                }
            }
            return value;
        }


        public  void WriteRegisterARM(byte a, ulong addr, ulong value, SerialPort sPort)
        {
            long test_value1 = (long)BaseRegisterMap.SPI_BASE_ADDR;
            uint test_value2 = (uint)SPIRegisterMap.SPI_FUNCTION_SELECT;
            uint FUNCT_SEL_ADDR = (uint)(test_value1 + test_value2);
            SendRegDataARM(0x1, FUNCT_SEL_ADDR, 0, sPort);
            int byte_sel = (int)(addr) & 0x3;
            byte_sel = byte_sel * 8;
            value = value & 0xff;
            value = value << byte_sel;
            SendRegDataARM(a, (uint)addr, (uint)value, sPort);
        }

        public  uint ReadRegisterARM(byte a, ulong addr, SerialPort sPort)
        {
            uint DATA = 0;
            long test_value1 = (long)BaseRegisterMap.SPI_BASE_ADDR;
            uint test_value2 = (uint)SPIRegisterMap.SPI_FUNCTION_SELECT;
            uint FUNCT_SEL_ADDR = (uint)(test_value1 + test_value2);
            SendRegDataARM(0x1, FUNCT_SEL_ADDR, 0, sPort);
            ReadRegDataARM(a, (uint)addr, ref DATA, sPort);
            return DATA;
        }

        public uint FlashReadStatus(SerialPort comm)
        {
            uint spi_shift_cnt;
            uint spi_rxvld_cnt;
            uint senddata;
            uint recvdata = 0;
            uint op_code = 0x05;
            //read stauts
            senddata = op_code;
            //0x82000010
            SendRegDataARM(0x1, TXD0_ADDR, senddata, comm);
            //SHIFT_CONTROL
            spi_shift_cnt = 16;
            spi_rxvld_cnt = 8;
            senddata = (3 << 16) + (spi_rxvld_cnt << 8) + (spi_shift_cnt << 0);
            //0x8200000c
            SendRegDataARM(0x1, SHIFT_ADDR, senddata, comm);
            //0x82000020
            ReadRegDataARM(0x11, RXD0_ADDR, ref recvdata, comm);
            return recvdata;
        }



        public uint ReadFlash(uint addr, SerialPort comm)
        {
            uint flash_address = addr;
            uint spi_shift_cnt;
            uint spi_rxvld_cnt;
            uint senddata;
            uint recvdata = 0;
            uint op_code = 0x03;

            SendRegDataARM(0x1, FUNCT_SEL_ADDR, 0x1, comm);
            Thread.Sleep(2);
            //TXD0
            senddata = (((flash_address & 0xff) << 24) + ((flash_address & 0xff00) << 8) + ((flash_address & 0xff0000) >> 8) + op_code);
            SendRegDataARM(0x1, TXD0_ADDR, senddata, comm);

            //SHIFT_CONTROL
            spi_shift_cnt = 64;
            spi_rxvld_cnt = 32;
            senddata = (3 << 16) + (spi_rxvld_cnt << 8) + (spi_shift_cnt << 0);
            SendRegDataARM(0x1, SHIFT_ADDR, senddata, comm);
            Thread.Sleep(20);

            ReadRegDataARM(0x11, RXD0_ADDR, ref recvdata, comm);
            return recvdata;
        }

        public void FlashWriteEnable(SerialPort comm)
        {

            uint spi_shift_cnt;
            uint spi_rxvld_cnt;
            uint senddata;
            uint op_code = 0x06;

            //打开写使能
            senddata = op_code;
            //0x82000010
            SendRegDataARM(0x1, TXD0_ADDR, senddata, comm);
            spi_shift_cnt = 8;
            spi_rxvld_cnt = 0;
            senddata = (3 << 16) + (spi_rxvld_cnt << 8) + (spi_shift_cnt << 0);
            //0x8200000c
            SendRegDataARM(0x1, SHIFT_ADDR, senddata, comm);
            Thread.Sleep(2);
        }


        public uint FastReadFlash(uint addr, SerialPort comm)
        {
            uint recvdata = 0;
            long test_value1 = (long)BaseRegisterMap.SPI_BASE_ADDR;

            uint test_value2 = (uint)SPIRegisterMap.SPI_FUNCTION_SELECT;
            uint FUNCT_SEL_ADDR = (uint)(test_value1 + test_value2);

            SendRegDataARM(0x1, FUNCT_SEL_ADDR, 0x0, comm);
            Thread.Sleep(2);
            ReadRegDataARM(0x11, 0x81000000 + addr, ref recvdata, comm);
            return recvdata;
        }

        public uint BigLittelEndian_Swap(uint data)
        {
            uint temp = 0;
            temp |= ((uint)(data) & 0xff000000) >> 24;
            temp |= ((uint)(data) & 0x00ff0000) >> 8;
            temp |= ((uint)(data) & 0x0000ff00) << 8;
            temp |= ((uint)(data) & 0x000000ff) << 24;
            return temp;
        }

        private void data_disp(byte[] sendbuf, int p)
        {
            var sb = BitConverter.ToString(sendbuf);
            if (!string.IsNullOrEmpty(sb.ToString()))
            {
                if (this.SendEvent != null)
                {
                    sb += " \r\n";
                    this.SendEvent(sb.ToString());
                }
            }
        }
        private void CommRecvBufClear(SerialPort comm)
        {
            if (comm.BytesToRead > 0) return;
            byte[] buf = new byte[9]; //声明一个临时数组存储当前来的串口数据  
            for (int i = 0; i < 100; i++)
            {
                if (comm.BytesToRead > 0)
                {
                    int n = comm.BytesToRead;//先记录下来，避免某种原因，人为的原因，操作几次之间时间长，缓存不一致  
                    if (n >= 8) n = 8;
                    comm.Read(buf, 0, n);//读取缓冲数据
                }
                else { break; }
            }
            buf = null;
        }

    }

}
